home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
090
/
byt86oct.arc
/
ALLOC.ARC
/
ALLOC4.MOD
< prev
next >
Wrap
Text File
|
1985-07-12
|
14KB
|
472 lines
IMPLEMENTATION MODULE Alloc4;
(* A storage allocator that tries to be safe about freed blocks.
It uses capabilities to keep track of blocks.
It also compacts space, and allows resizing.
A capability is a generation count and an offset into the heap. The offset
is used to find the master capability.
Copyright 1986 by Jonathan Amsterdam. All Rights Reserved. *)
FROM SYSTEM IMPORT WORD, ADDRESS, TSIZE, ADR;
FROM MachineSpecific IMPORT getHeapBottom, getHeapTop, bytesPerWord,
address, cardinal, addrLessThan, writeAddress, addWords, subtractWords,
maxAddress;
FROM MyTerminal IMPORT fatal, WriteLnString, WriteCard,
WriteString, WriteLn;
CONST maxIndex = 32767;
nMasters = 10; (* number of masters to allocate each time more needed *)
TYPE capability = ADDRESS;
capRec = RECORD (* used to get the components of a capability *)
CASE BOOLEAN OF
TRUE: genCount:CARDINAL;
offset: CARDINAL;
| FALSE: cap:capability;
END;
END;
handle = POINTER TO masterCap;
blockPtr = POINTER TO block;
masterCap = RECORD
genCount:CARDINAL;
CASE BOOLEAN OF
TRUE: nextMaster:handle;
| FALSE: blockp:blockPtr;
END;
END;
block = RECORD
size:CARDINAL; (* not including header *)
CASE BOOLEAN OF
TRUE: nextBlock: blockPtr;
| FALSE: contents:ARRAY[0..maxIndex] OF WORD;
END;
END;
VAR heapBottom, (* first word in heap *)
heapTop, (* last word in heap *)
masterBottom, (* lowest point of masters section *)
firstMaster:ADDRESS; (* first master ever allocated *)
freeList:blockPtr; (* start of free list *)
masterPtr, (* next available master *)
masterFreeList:handle; (* start of master free list *)
blockHeaderWords, (* # of words in a block header *)
minBlockSize, (* smallest value for size field of a block *)
masterWords:CARDINAL; (* # of words occupied by a master capability *)
cr:capRec; (* a dummy record used for capabilities *)
PROCEDURE init;
VAR heapWords:CARDINAL;
BEGIN
heapBottom := getHeapBottom();
heapTop := getHeapTop();
blockHeaderWords := TSIZE(CARDINAL);
masterWords := TSIZE(masterCap);
minBlockSize := TSIZE(blockPtr);
freeList := blockPtr(heapBottom);
heapWords := cardinal(heapTop - heapBottom + address(1)) DIV bytesPerWord;
freeList^.size := heapWords - blockHeaderWords;
freeList^.nextBlock := NIL;
masterBottom := oneAfter(freeList);
firstMaster := subtractWords(masterBottom, masterWords);
masterPtr := handle(firstMaster);
masterFreeList := NIL;
moreMasters;
END init;
PROCEDURE oneAfter(blockp:blockPtr):ADDRESS;
(* Returns the address of 1 higher than block *)
BEGIN
RETURN addWords(blockp, blockp^.size + blockHeaderWords);
END oneAfter;
PROCEDURE blockSize(c:capability):CARDINAL;
VAR blockp:blockPtr;
BEGIN
blockp := getBlock(c);
RETURN blockp^.size;
END blockSize;
PROCEDURE getWord(c:capability; n:CARDINAL):WORD;
VAR blockp:blockPtr;
BEGIN
blockp := getBlock(c);
accessCheck(blockp, n);
RETURN blockp^.contents[n];
END getWord;
PROCEDURE setWord(c:capability; n:CARDINAL; w:WORD);
VAR blockp:blockPtr;
BEGIN
blockp := getBlock(c);
accessCheck(blockp, n);
blockp^.contents[n] := w;
END setWord;
PROCEDURE getBlock(c:capability):blockPtr;
VAR master:handle;
BEGIN
master := getMaster(c);
RETURN master^.blockp;
END getBlock;
PROCEDURE getMaster(c:capability):handle;
VAR cr:capRec;
master:handle;
BEGIN
cr.cap := c;
master := handle(subtractWords(firstMaster, cr.offset));
IF cr.genCount <> master^.genCount THEN
fatal('generation counts disagree');
ELSE
RETURN master;
END;
END getMaster;
PROCEDURE accessCheck(blockp:blockPtr; n:CARDINAL);
BEGIN
IF n >= blockp^.size THEN
fatal('access out of bounds');
END;
END accessCheck;
PROCEDURE allocate(nWords:CARDINAL):capability;
VAR cr:capRec;
master:handle;
BEGIN
master := allocMaster();
IF master <> NIL THEN
master^.blockp := NIL; (* do this first to prevent this master from
being involved in compaction *)
master^.blockp := allocBlock(nWords);
END;
cr.genCount := master^.genCount;
cr.offset := cardinal(firstMaster - ADDRESS(master)) DIV bytesPerWord;
RETURN cr.cap;
END allocate;
PROCEDURE allocBlock(nWords:CARDINAL):blockPtr;
VAR blockp:blockPtr;
BEGIN
blockp := allocB(nWords);
IF blockp = NIL THEN
compact;
blockp := allocB(nWords);
END;
RETURN blockp;
END allocBlock;
PROCEDURE allocB(nWords:CARDINAL):blockPtr;
VAR currBlock, prevBlock, newBlock:blockPtr;
blockWords:CARDINAL;
BEGIN
IF nWords < minBlockSize THEN
nWords := minBlockSize; (* can't allocate a smaller block than this *)
END;
blockWords := nWords + blockHeaderWords;
currBlock := freeList;
prevBlock := NIL;
WHILE currBlock <> NIL DO
IF blockWords + minBlockSize <= currBlock^.size THEN
(* split the block into two, returning the 1st part *)
newBlock := addWords(currBlock, blockWords);
newBlock^.size := currBlock^.size - blockWords;
newBlock^.nextBlock := currBlock^.nextBlock;
link(prevBlock, newBlock);
currBlock^.size := nWords;
RETURN currBlock;
ELSIF nWords <= currBlock^.size THEN (* return the whole block *)
link(prevBlock, currBlock^.nextBlock);
RETURN currBlock;
END;
prevBlock := currBlock;
currBlock := currBlock^.nextBlock;
END (* WHILE *);
RETURN NIL;
END allocB;
PROCEDURE allocMaster():handle;
(* The strategy here is as follows:
1. If the master free list isn't empty, take the first master.
2. If there is enough room between masterBottom and masterPtr to allocate
a master, do so.
3. If that fails, compact and allocate more masters, then try again.
*)
VAR h:handle;
BEGIN
IF masterFreeList <> NIL THEN
h := masterFreeList;
masterFreeList := masterFreeList^.nextMaster;
RETURN h;
ELSE
IF addrLessThan(masterPtr, masterBottom) THEN
compact;
moreMasters;
END;
IF addrLessThan(masterPtr, masterBottom) THEN
RETURN NIL;
ELSE
masterPtr^.genCount := 0;
masterPtr := handle(subtractWords(masterPtr, masterWords));
RETURN addWords(masterPtr, masterWords);
END;
END;
END allocMaster;
PROCEDURE moreMasters;
(* Get highest block. If its top isn't contiguous with the masters already
allocated, do nothing.
Else, try to allocate nMasters from its top; if it's too
small, allocate it all.
*)
VAR prev, high:blockPtr;
nWords:CARDINAL;
BEGIN
nWords := nMasters * masterWords;
IF freeList <> NIL THEN
high := freeList;
prev := NIL;
WHILE high^.nextBlock <> NIL DO
prev := high;
high := high^.nextBlock;
END;
(* high now points to highest block *)
IF oneAfter(high) = masterBottom THEN
(* top of block is contiguous with masters *)
IF high^.size >= minBlockSize + nWords THEN
(* chop off nWords words from high *)
DEC(high^.size, nWords);
masterBottom := oneAfter(high);
ELSIF high^.size >= minBlockSize + masterWords THEN
(* chop of enough for one master *)
DEC(high^.size, masterWords);
masterBottom := oneAfter(high);
ELSE
(* detach whole block *)
link(prev, high^.nextBlock);
masterBottom := high;
END;
END;
END;
END moreMasters;
PROCEDURE free(VAR c:capability);
(* Return the block to the free list; put the master on the master free list.*)
VAR master:handle;
BEGIN
master := getMaster(c);
freeBlk(master^.blockp);
INC(master^.genCount);
master^.nextMaster := masterFreeList;
masterFreeList := master;
END free;
PROCEDURE freeBlk(freeBlock:blockPtr);
VAR currBlock, prevBlock:blockPtr;
BEGIN
IF NOT addrBetween(heapBottom, freeBlock, masterBottom) THEN
fatal("free: block not in heap");
ELSIF freeBlock = NIL THEN
fatal("free: attempt to free an already freed block");
ELSE
currBlock := freeList;
prevBlock := NIL;
WHILE (currBlock <> NIL) AND addrLessThan(currBlock, freeBlock) DO
prevBlock := currBlock;
currBlock := currBlock^.nextBlock;
END;
IF currBlock = NIL THEN
freeBlock^.nextBlock := NIL;
link(prevBlock, freeBlock);
ELSE (* freeBlock belongs just before currBlock *)
freeBlock^.nextBlock := currBlock;
link(prevBlock, freeBlock);
END;
tryToMerge(prevBlock, freeBlock, currBlock);
END;
END freeBlk;
PROCEDURE tryToMerge(lowBlock, middleBlock, highBlock:blockPtr);
BEGIN
IF adjacent(middleBlock, highBlock) THEN
merge(middleBlock, highBlock);
END;
IF adjacent(lowBlock, middleBlock) THEN (* this should be impossible *)
merge(lowBlock, middleBlock);
END;
END tryToMerge;
PROCEDURE adjacent(lowerBlock, higherBlock:blockPtr):BOOLEAN;
BEGIN
RETURN
(lowerBlock <> NIL) AND
(higherBlock <> NIL) AND
(oneAfter(lowerBlock) = higherBlock);
END adjacent;
PROCEDURE merge(lowerBlock, higherBlock:blockPtr);
BEGIN
INC(lowerBlock^.size, higherBlock^.size + blockHeaderWords);
lowerBlock^.nextBlock := higherBlock^.nextBlock;
END merge;
PROCEDURE resize(c:capability; nWords:CARDINAL);
VAR blockp:blockPtr;
master:handle;
BEGIN
master := getMaster(c);
blockp := allocBlock(nWords);
IF blockp <> NIL THEN
copyFromTo(master^.blockp, blockp, nWords);
freeBlk(master^.blockp);
master^.blockp := blockp;
END;
END resize;
PROCEDURE compact;
(* compact blocks to low end of heap *)
VAR lowPoint:blockPtr;
lowestHandle:handle;
BEGIN
IF freeList <> NIL THEN
lowPoint := heapBottom;
WHILE findLowestHandleNotLowerThan(lowPoint, lowestHandle) DO
IF lowestHandle^.blockp <> lowPoint THEN
lowPoint^.size := lowestHandle^.blockp^.size;
copyFromTo(lowestHandle^.blockp, lowPoint, lowPoint^.size);
lowestHandle^.blockp := lowPoint;
END;
lowPoint := oneAfter(lowPoint);
END;
(* now fix freelist *)
freeList := lowPoint;
freeList^.size := (cardinal(masterBottom-ADDRESS(freeList))
DIV bytesPerWord) - blockHeaderWords;
freeList^.nextBlock := NIL;
END;
END compact;
PROCEDURE findLowestHandleNotLowerThan(low:blockPtr;VAR min:handle):BOOLEAN;
(* The IF condition in the loop checks three things: 1. the handle under
consideration is <= than the current minimum; 2. it is >= the low point;
3. it is < masterBottom (hence not part of the master free list).
*)
VAR h:handle;
return:BOOLEAN;
mc: masterCap;
BEGIN
h := firstMaster;
mc.blockp := blockPtr(maxAddress);
min := ADR(mc);
return := FALSE;
WHILE addrLessThan(masterPtr, h) DO
IF (NOT addrLessThan(min^.blockp, h^.blockp)) AND
(NOT addrLessThan(h^.blockp, low)) AND
addrLessThan(h^.blockp, masterBottom) THEN
min := h;
return := TRUE;
END;
h := subtractWords(h, masterWords);
END;
RETURN return;
END findLowestHandleNotLowerThan;
PROCEDURE copyFromTo(source, dest:blockPtr; nWords:CARDINAL);
VAR i:CARDINAL;
BEGIN
IF source^.size < nWords THEN
nWords := source^.size;
END;
FOR i := 0 TO nWords-1 DO
dest^.contents[i] := source^.contents[i];
END;
END copyFromTo;
PROCEDURE link(prevBlock, linkBlock:blockPtr);
BEGIN
IF prevBlock = NIL THEN
freeList := linkBlock;
ELSE
prevBlock^.nextBlock := linkBlock;
END;
END link;
PROCEDURE addrBetween(low, middle, high:ADDRESS):BOOLEAN;
BEGIN
RETURN (addrLessThan(low, middle) OR (low = middle)) AND
(addrLessThan(middle, high) OR (middle = high));
END addrBetween;
(*** debugging stuff ***)
PROCEDURE getFreeList():capability;
(* for debugging only *)
BEGIN
RETURN capability(freeList);
END getFreeList;
PROCEDURE writeMap;
VAR lowestFree, lowPoint:blockPtr;
lowestAlloc:handle;
PROCEDURE writeFree;
BEGIN
WriteString("Free ");
writeRelAddress(lowestFree);
WriteCard(lowestFree^.size, 4);
WriteLnString(" words");
END writeFree;
BEGIN (* writeMap *)
WriteLn;
lowestFree := freeList;
lowPoint := heapBottom;
WHILE findLowestHandleNotLowerThan(lowPoint, lowestAlloc) DO
WHILE addrLessThan(lowestFree, lowestAlloc^.blockp)
AND (lowestFree <> NIL) DO
writeFree;
lowestFree := lowestFree^.nextBlock;
END;
WriteString("Alloc ");
writeRelAddress(lowestAlloc^.blockp);
WriteCard(lowestAlloc^.blockp^.size, 4);
WriteString(" words; gen. count = ");
WriteCard(lowestAlloc^.genCount, 0); WriteLn;
lowPoint := oneAfter(lowestAlloc^.blockp);
END;
WHILE lowestFree <> NIL DO
writeFree;
lowestFree := lowestFree^.nextBlock;
END;
WriteLn;
WriteLnString("master free list:");
lowestAlloc := masterFreeList;
WHILE lowestAlloc <> NIL DO
writeRelAddress(lowestAlloc);
lowestAlloc := lowestAlloc^.nextMaster;
END;
WriteLn;
WriteString("firstMaster: ");
writeRelAddress(firstMaster); WriteLn;
WriteString("masterPtr: ");
writeRelAddress(masterPtr); WriteLn;
WriteString("masterBottom: ");
writeRelAddress(masterBottom); WriteLn;
END writeMap;
PROCEDURE writeRelAddress(a:ADDRESS);
BEGIN
WriteCard(cardinal(a - heapBottom), 4);
END writeRelAddress;
BEGIN
init;
END Alloc4.
ddress(a:ADDRESS);
BEGIN
WriteCard(cardinal(a - heapBottom), 4);
END writeRelAddress;